using System;
using System.IO;
using System.Security.Cryptography;

namespace ProtoScratch.Common
{
	/// <summary>
	/// The scratch encryption object is for negotiating encryption keys and encrypting/decrypting network data
	/// </summary>
	/// <exception cref='ArgumentNullException'>
	/// Is thrown when an argument passed to a method is invalid because it is <see langword="null" /> .
	/// </exception>
	public class ScratchEncryption
	{
		private RijndaelManaged rijAlg;
		public byte[] encKey 
		{ 
			get { return rijAlg.Key; }
			set { rijAlg.Key = value; }
		}
		public byte[] encIV 
		{ 
			get { return rijAlg.IV; } 
			set { rijAlg.IV = value; }
		}
		
		public RSAParameters rsaKey { get; set; }

		public ScratchEncryption ()
		{
			rijAlg = new RijndaelManaged();
			rijAlg.GenerateKey();
			rijAlg.GenerateIV();
		}
		
		public byte[] RSAEncrypt(byte[] Raw)
		{
			var RSA = new RSACryptoServiceProvider();
			RSA.ImportParameters(rsaKey);
			return RSA.Encrypt(Raw, false);
		}
		
		public byte[] RSADecrypt(byte[] Raw)
		{
			var RSA = new RSACryptoServiceProvider();
			RSA.ImportParameters(rsaKey);
			return RSA.Decrypt(Raw, false);
		}
		
		/// <summary>
		/// Returns the encrypted version of the raw data
		/// </summary>
		/// <param name='Raw'>
		/// Raw data.
		/// </param>
		public byte[] Encrypt(byte[] Raw)
		{
			if (Raw == null || Raw.Length <=0) return Raw;
			byte[] encrypted;

            ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);

            using (MemoryStream msEncrypt = new MemoryStream())
            {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    csEncrypt.Write(Raw, 0, Raw.Length);
                }
				encrypted = msEncrypt.ToArray();
            }
			
			return encrypted;
		}
		
		/// <summary>
		/// Returns the decrypted version of the raw data
		/// </summary>
		/// <param name='Raw'>
		/// Raw data.
		/// </param>
		public byte[] Decrypt(byte[] Raw)
		{
			if (Raw == null || Raw.Length <=0) return Raw;
			byte[] decrypted = new byte[Raw.Length];
			
            ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);

            using (MemoryStream msDecrypt = new MemoryStream(Raw))
            {
                using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    csDecrypt.Read(decrypted,0,Raw.Length);
                }
            }
			
			return decrypted;
		}
	}
}

